import {
    _decorator,
    Component,
    Event,
    EventTouch,
    Material,
    math,
    Node,
    Sprite,
    Touch,
    tween,
    UITransform,
    v2,
    Vec2,
} from 'cc';
const { ccclass, property } = _decorator;

enum Direction {
    top,
    bottom,
    left,
    right,

    topRight,
    bottomRight,
    topLeft,
    bottomLeft,
}

enum MtType {
    page,
    skew,
}

const DirectionString = [
    'top',
    'bottom',
    'left',
    'right',

    'topRight',
    'bottomRight',
    'topLeft',
    'bottomLeft',
];

const directionMap = new Map<string, Direction>();

@ccclass('TouchMove')
export class TouchMove extends Component {
    @property(Node)
    top: Node = null!;

    @property(Node)
    bottom: Node = null!;

    @property(Node)
    left: Node = null!;

    @property(Node)
    right: Node = null!;

    //斜角
    @property(Node)
    topRight: Node = null!;

    @property(Node)
    bottomRight: Node = null!;

    @property(Node)
    topLeft: Node = null!;

    @property(Node)
    bottomLeft: Node = null!;

    @property(Material)
    mtPage: Material = null!;

    @property(Material)
    mtPageSkew: Material = null!;

    startPos = new Vec2(0, 0);
    endPos = new Vec2(0, 0);
    moveDistance: number = 0;

    touchDir = v2();

    distance1: number = 0;

    dir = v2();
    sign = 1;

    flipV2 = v2();
    maxDis = 0;

    mtType = MtType.page;

    nodeArr: Node[] = [];

    protected onLoad(): void {
        this.nodeArr = [
            this.top,
            this.bottom,
            this.left,
            this.right,
            this.topLeft,
            this.topRight,
            this.bottomLeft,
            this.bottomRight,
        ];

        this.nodeArr.forEach((n, i) => {
            this.addListener(n);
            directionMap.set(DirectionString[i], i as Direction);
        });
    }

    addListener(node: Node) {
        node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
        node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
        node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    removeListener(node: Node) {
        node.off(Node.EventType.TOUCH_START, this.onTouchStart, this);
        node.off(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        node.off(Node.EventType.TOUCH_END, this.onTouchEnd, this);
        node.off(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    protected onDestroy(): void {
        this.nodeArr.forEach((n, i) => {
            this.removeListener(n);
        });
    }

    startTouch() {
        this.getTouchPage(DirectionString[Direction.right]);
        this.setPageDistance(0);
    }

    private initMetalPage() {
        this.setMetal(this.mtPage);
        this.mtType = MtType.page;
    }

    private initMetalSkew() {
        this.setMetal(this.mtPageSkew);
        this.mtType = MtType.skew;
    }

    private onTouchStart(touch: EventTouch) {
        this.startPos = touch.getLocation();
        this.endPos = touch.getLocation();
        this.getTouchPage(touch.currentTarget.name);
    }

    private onTouchMove(touch: Touch) {
        const tr = this.node.getComponent(UITransform)!;
        let diff = v2();
        Vec2.subtract(diff, this.startPos, this.endPos);
        let moveToDir = Vec2.dot(this.touchDir, diff.normalize());
        this.endPos = touch.getLocation();
        if (moveToDir < 0) {
            return;
        }
        const dis = math.clamp(
            Vec2.distance(this.startPos, this.endPos) / (tr.width / 2),
            0,
            1,
        );
        this.setDistance(dis);
        if (dis >= 0.9) {
            this.onTouchEnd(touch);
        }
    }

    setDistance(dis: number) {
        this.moveDistance = dis;
        switch (this.mtType) {
            case MtType.page:
                this.setPageDistance(dis);
                break;
            case MtType.skew:
                this.setSkewDistance(dis);
                break;
        }
    }

    setPageDistance(dis: number) {
        this.distance1 = dis;
        let distance1: number;
        if (this.sign > 0) {
            distance1 = math.clamp(Math.abs(1.0 - dis), 0.33, 1);
        } else {
            distance1 = math.clamp(dis * this.sign, 0, -0.66);
        }
        this.setProperty('mouseDir', this.dir);
        this.setProperty('distance1', distance1);
    }

    setSkewDistance(dis: number) {
        this.distance1 = dis * 0.5;
        let distance1: number = this.maxDis - math.clamp(this.distance1, 0, 0.5);
        this.setProperty('distance1', distance1);
        this.setProperty('flipV2', this.flipV2);
        this.setProperty('mouseDir', this.dir);
        this.setProperty('maxDis', this.maxDis);
    }

    onTouchEnd(touch: Touch) {
        if (Math.abs(this.moveDistance) > 0.5) {
            this.toEndTween();
        } else {
            this.toStartTween();
        }

        this.startPos = v2();
        this.endPos = v2();
    }

    //还原动画
    toStartTween() {
        let dis = this.moveDistance;
        tween({ dis })
            .to(
                0.2,
                { dis: 0 },
                {
                    onUpdate: (target, ratio) => {
                        const { dis } = target as { dis: number };
                        this.setDistance(dis);
                    },
                },
            )
            .start();
    }

    //
    toEndTween() {
        let dis = this.moveDistance;
        tween({ dis })
            .to(
                0.1,
                { dis: 1 },
                {
                    onUpdate: (target, ratio) => {
                        const { dis } = target as { dis: number };
                        this.setDistance(dis);
                    },
                },
            )
            .call(() => {
                this.node.emit('page-open');
            })
            .start();
    }

    toShowCard() {
        tween(this.dir)
            .to(
                0.2,
                { x: 0, y: 0 },
                {
                    onUpdate: (target, ratio) => {
                        this.setProperty('mouseDir', this.dir);
                    },
                },
            )
            .start();
    }

    getTouchPage(name: string) {
        let dir = directionMap.get(name);
        console.log('dir:', dir);

        switch (dir) {
            case Direction.top:
                this.dir = v2(0, -1);
                this.touchDir.set(0, 1);
                this.initMetalPage();
                break;
            case Direction.bottom:
                this.dir = v2(0, 1);
                this.touchDir.set(0, -1);
                this.initMetalPage();
                break;
            case Direction.left:
                this.dir = v2(-1, 0);
                this.touchDir.set(-1, 0);
                this.initMetalPage();
                break;
            case Direction.right:
                this.dir = v2(1, 0);
                this.touchDir.set(1, 0);
                this.initMetalPage();
                break;
            case Direction.topLeft:
                this.touchDir.set(-1, 1);
                this.dir = v2(-0.5, -0.5);
                this.flipV2 = v2(0, 0);
                this.maxDis = 0;
                this.initMetalSkew();
                break;
            case Direction.topRight:
                this.touchDir.set(1, 1);
                this.dir = v2(0.5, -0.5);
                this.flipV2 = v2(1, 0);
                this.maxDis = 0.5;
                this.initMetalSkew();
                break;
            case Direction.bottomLeft:
                this.touchDir.set(-1, -1);
                this.dir = v2(-0.5, 0.5);
                this.flipV2 = v2(0, 1);
                this.maxDis = 0.5;
                this.initMetalSkew();
                break;
            case Direction.bottomRight:
                this.touchDir.set(1, -1);
                this.dir = v2(0.5, 0.5);
                this.flipV2 = v2(1, 1);
                this.maxDis = 1;
                this.initMetalSkew();
                break;

            default:
                break;
        }
        this.sign = Vec2.dot(this.dir, v2(1, 1));
    }

    getMoveDir() {
        let moveDir = v2(
            this.startPos.x - this.endPos.x,
            this.startPos.x - this.endPos.y,
        ).normalize();
        if (Math.abs(moveDir.x) > Math.abs(moveDir.y)) {
            if (moveDir.x > 0) {
                return v2(1, 0);
            } else {
                return v2(-1, 0);
            }
        } else {
            if (moveDir.y > 0) {
                return v2(0, 1);
            } else {
                return v2(0, -1);
            }
        }
    }

    setMetal(mt: Material) {
        const sp = this.node.getComponent(Sprite)!;
        if (mt === sp.customMaterial) {
            return;
        }
        sp.customMaterial = mt;
    }

    setProperty(property: string, data: any) {
        const sp = this.node.getComponent(Sprite)!;
        const meta = sp.customMaterial!;

        meta.setProperty(property, data);
    }
}
